import { ClassDeclaration, createSourceFile, Decorator, ExpressionWithTypeArguments, Identifier, ObjectLiteralElement, ObjectLiteralExpression, ScriptTarget, SyntaxKind } from 'typescript';
import { CallExpression, Project, Scope, SourceFile, VariableDeclarationKind } from "ts-morph";
import { dtoNameTransfer } from '.';

/**
 * 生成zod定义
 */
export function generateZod(options: {
  /** 源文件 */
  filename: string
  /** dto类 ast对象 */
  dto: ClassDeclaration
  /** 源文件文本，用于截取内容 */
  originContent: string
}) {
  const { filename, dto, originContent } = options;

  /** 原来的dto类名 */
  const className: string = dto.name?.escapedText as string;
  if (!className) return;

  /** 命名转换 */
  const zodName = dtoNameTransfer(className);

  /** 内存中创建新的ts-morph对象 */
  const sourceFile = new Project().createSourceFile(filename, "");

  /** 
   * 类属性成员列表
   */
  const propertyMembers = dto.members.filter(item => 
    item.kind == SyntaxKind.PropertyDeclaration && (item as unknown as {modifiers: Decorator[]}).modifiers?.find(item => (<any>item.expression)?.expression?.escapedText == 'ApiProp')
  );
  const zodItmesDefineContent = propertyMembers.map(property => {
    const apiPropDecorator = (property as unknown as {modifiers: Decorator[]}).modifiers.find(item => (<any>item.expression)?.expression?.escapedText == 'ApiProp')!;
    const firstArgument: ObjectLiteralExpression = (apiPropDecorator.expression as any).arguments[0];
    const descProperty = firstArgument.properties.find(item => (<Identifier>item.name).escapedText === 'desc');
    const zodTypeProperty = firstArgument.properties.find(item => (<Identifier>item.name).escapedText === 'zodType');
    //@ts-ignore
    const propName = property.name.escapedText;
    //@ts-ignore
    const TsType = originContent.slice(property.type.pos, property.type.end)
    //@ts-ignore
    const desc = descProperty?.initializer.text;
    //@ts-ignore
    const zodType = zodTypeProperty?.initializer ? originContent.slice(zodTypeProperty?.initializer?.pos, zodTypeProperty?.initializer?.end) : `z.${TsType}()`;
    return `
      /** ${desc} */
      ${propName}: ${zodType},
    `
  }).join('\n');

  /**
   * const xxxZod = z.object
   */
  sourceFile.addVariableStatement({
    isExported: true,
    declarationKind: VariableDeclarationKind.Const,
    declarations: [
      {
        name: zodName,
        initializer: writer => {
          writer.writeLine("z.object({");
          writer.write(zodItmesDefineContent);
          writer.writeLine("})");
        }
      }
    ]
  })

  sourceFile.formatText({
    indentSize: 2,
    convertTabsToSpaces: true,
  });

}
